package com.beantools.util;

import com.beantools.factory.reflect.CommonReflectFactory;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

import static com.beantools.constants.SingleChar.BLANK_OP;
import static com.beantools.constants.SingleChar.NULL_OP;
import static com.beantools.util.ObjectUtil.isNull;
import static com.beantools.util.StringUtil.isBlank;
import static com.beantools.util.StringUtil.isNotBlank;

/**
 * 这是操作数组的工具类
 *
 * @author baoya
 * @version 1.0
 * @since 1.1.0
 */
public class ArrayUtil {


    /**
     * 判断数组是否为null，如果数组为空或者数组长度为0，直接返回true。
     *
     * @param objects 对象数组
     * @return 数组为null，则返回true，否则，返回false
     */
    public static boolean isEmpty(Object[] objects) {
        return null == objects || objects.length == 0;
    }

    /**
     * 判断数组是不为null，如果数组不为空且数组长度为0，直接返回true。
     *
     * @param objects 对象数组
     * @return 数组为null，则返回true，否则，返回false
     */
    public static boolean isNotEmpty(Object[] objects) {
        return null != objects && objects.length > 0;
    }

    /**
     * 如果数组中的可变数组都为空，其返回true，否则，返回false
     *
     * @param objects 数组的可变数组
     * @return true|false
     */
    public static boolean isAllEmpty(Object[]... objects) {
        if (null == objects || objects.length == 0) {
            return true;
        }
        for (Object[] object : objects) {
            if (isNotEmpty(object)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 如果数组中的可变数组都不为空，其返回true，否则，返回false
     *
     * @param objects 数组的可变数组
     * @return true|false
     */
    public static boolean isAllNotEmpty(Object[]... objects) {
        if (null == objects && objects.length == 0) {
            return false;
        }
        for (Object[] object : objects) {
            if (isEmpty(object)) {
                return false;
            }
        }
        return true;
    }


    /**
     * 判断数组是否为null，不仅要判断数组本身，还要判断数组内部子元素。
     * 如果数组为空或者数组长度为0，直接返回true；如果数组元素不为空，但
     * 其内部所有值都为空值，此时，就会返回true，否则，返回false
     *
     * @param objects 对象数组
     * @return 数组为null，则返回true，否则，返回false
     */
    public static boolean containNullObject(Object[] objects) {
        if (null == objects || objects.length == 0) {
            return true;
        }
        boolean nullObject = false;
        for (Object object : objects) {
            if (isNull(object)) {
                nullObject = true;
                break;
            }
        }
        return nullObject;
    }


    /**
     * 判断数组不为null，不仅要判断数组本身，还要判断数组内部子元素。
     * 如果数组为空，或者，数组不为空但数组长度为0，直接返回false；如果
     * 数组元素不为空，但其内部至少有一个值为空值，此时返回true，否则，返回false
     *
     * @param objects 待验证的对象数组
     * @return 验证成功，返回true，否则，返回false
     */
    public static boolean isNoNullObject(Object[] objects) {
        boolean nullTrue = null == objects || (null != objects && objects.length == 0);
        if (nullTrue) {
            return false;
        }
        boolean noNullObject = true;
        for (Object object : objects) {
            if (object instanceof String) {
                noNullObject = isNotBlank((String) object);
            }
            if (null == object) {
                noNullObject = false;
                break;
            }
        }
        return noNullObject;
    }

    /**
     * 这是通过一定的格式去连接数组的方法，比如：
     * <pre>
     *     <code>
     *          String[] strs={"english=WEDNESDAY","chinese=星期三"};
     *          String str=ArrayUtil.join(strs,"&");
     *          output:
     *              english=WEDNESDAY&chinese=星期三
     *       </code>
     * </pre>
     *
     * @param strings 待拼接的字符串数组
     * @param concat  拼接字符
     * @return 返回的是拼接后的字符串
     */
    public static String join(String[] strings, String concat) {
        if (containNullObject(strings)) {
            throw new IllegalArgumentException("待拼接的数组不能存有空字符串");
        }
        if (isBlank(concat)) {
            throw new IllegalArgumentException("拼接的字符串不能为空");
        }
        StringBuilder strJoined = new StringBuilder();
        for (String string : strings) {
            strJoined.append(string + concat);
        }
        return strJoined.substring(0, strJoined.lastIndexOf(concat));
    }

    /**
     * 这是通过一定的格式去连接容器的方法，比如：
     * <pre>
     *      Set set = new HashSet();
     *      set.add("english=WEDNESDAY");
     *      set.add("chinese=星期三");
     *   output:
     *       english=WEDNESDAY&chinese=星期三
     * </pre>
     *
     * @param collection 待拼接的字符串容器
     * @param concat     拼接字符
     * @return 返回的是拼接后的字符串
     */
    public static String join(Collection<String> collection, String concat) {
        if (CollectionUtil.isEmpty(collection)) {
            throw new IllegalArgumentException("待拼接的数组不能存有空字符串");
        }
        String[] strings = ArrayUtil.toStringArray(collection);
        return join(strings, concat);
    }

    /**
     * 将容器对象转化字符串数组。
     *
     * @param collection 容器对象。
     * @return 返回字符串数组。包括空对象、空格字符串、空字符串。
     */
    public static String[] toStringArray(final Collection<String> collection) {
        String[] var = new String[initArrayCapacity(collection)];
        int index = 0;
        for (String string : collection) {
            var[index++] = string;
        }
        return var;

    }

    /**
     * 将容器对象转化字符串数组。
     *
     * @param collection 容器对象。
     * @return 返回字符串数组。包括空对象、空格字符串、空字符串。
     */
    public static String[] toStringArrayRidOfUseless(final Collection<String> collection) {
        String[] var = new String[initArrayCapacity(collection)];
        int index = 0;
        for (String string : collection) {
            if (isBlank(string)) {
                continue;
            }
            String var1 = string.replaceAll(BLANK_OP, NULL_OP);
            if (isBlank(var1)) {
                continue;
            }
            var[index++] = var1;
        }
        return var;

    }


    /**
     * 将容器对象转化字符串数组。
     *
     * @param collection 容器对象。
     * @return 返回字符串数组。包括空对象、空格字符串、空字符串。
     */
    public static String[] toStringArrayRidOfNull(final Collection<String> collection) {
        String[] var = new String[initArrayCapacity(collection)];
        int index = 0;
        for (String string : collection) {
            if (StringUtil.isEmpty(string)) {
                continue;
            }
            var[index++] = string;
        }
        return Arrays.copyOf(var, index);
    }

    /**
     * 将容器对象转化字符串数组。
     *
     * @param collection 容器对象。
     * @return 返回字符串数组。包括空对象、空格字符串、空字符串。
     */
    public static String[] toStringArrayRidOfWhitespace(final Collection<String> collection) {
        String[] var = new String[initArrayCapacity(collection)];
        int index = 0;
        for (String string : collection) {
            if (StringUtil.isBlank(string)) {
                continue;
            }
            var[index++] = string;
        }
        return Arrays.copyOf(var, index);
    }

    /**
     * 数组去空操作。数组里面可能含有空值，这时，需要做去空值操作。
     *
     * @param ele   可能包含空字符的数组
     * @param clazz 当前数组中存放的类
     * @return 去除空对象的类
     */
    public static <E> E[] removeNull(E[] ele, Class<? extends E> clazz) {
        if (ArrayUtil.isEmpty(ele)) {
            return null;
        }
        if (ArrayUtil.isNoNullObject(ele)) {
            return ele;
        }
        int initCapacity = 5;
        int index = 0;
        E[] newEArray = (E[]) Array.newInstance(clazz, initCapacity);
        for (E e : ele) {
            if (isNull(e)) {
                continue;
            }
            if (index < initCapacity) {
                newEArray[index++] = e;
            } else {
                initCapacity = initCapacity << 1 + 2;
                newEArray = Arrays.copyOf(newEArray, initCapacity);
            }
        }
        return Arrays.copyOfRange(newEArray, 0, index);
    }

    /**
     * 将泛型数组转化为List容器，可以转化为{@code LinkedList}和{@code ArrayList}接口
     * <pre>
     *        Person[] people = new Person[2];
     *        people[0] = new Person("zhubaoaya1", "11111111");
     *        people[1] = new Person("zhubaoaya2", "22222222");
     *        List<Person> people1 = makeArrayColl(people);
     * </pre>
     *
     * @param ele      带转化泛型数组
     * @param clazz    泛型尖括号中的的类字面常量
     * @param isLinked 是否是链表集合
     * @return 泛型对象容器
     * @throws IllegalArgumentException 如果容器为空或者类字面常量为空，则会抛出异常
     * @throws NoSuchMethodException    当前类没有默认的构造器
     */
    @SuppressWarnings("all")
    public static <E> List<E> asList(E[] ele, Class<? extends E> clazz, boolean isLinked) throws IllegalArgumentException, NoSuchMethodException {
        if (isNull(clazz)) {
            throw new IllegalArgumentException("类字面不能为空");
        }
        if (ArrayUtil.containNullObject(ele)) {
            ele = ArrayUtil.removeNull(ele, clazz);
        }
        if (ArrayUtil.isEmpty(ele)) {
            return null;
        }
        List list = isLinked ? new LinkedList() : new ArrayList(ele.length);
        Constructor defaultConstruct = CommonReflectFactory.getInstance(clazz).getNoArgsConstruct(null);
        for (E e : ele) {
            try {
                E e1 = (E) defaultConstruct.newInstance();
                e1 = e;
                list.add(e1);
            } catch (InstantiationException ex) {
                ex.printStackTrace();
            } catch (IllegalAccessException ex) {
                ex.printStackTrace();
            } catch (InvocationTargetException ex) {
                ex.printStackTrace();
            }
        }
        return list;
    }

    /**
     * 将泛型数组转化为List容器，可以转化为{@code LinkedHashSet}和{@code HashSet}接口
     * <pre>
     *        Person[] people = new Person[2];
     *        people[0] = new Person("zhubaoaya1", "11111111");
     *        people[1] = new Person("zhubaoaya2", "22222222");
     *        Set<Person> people1 = makeArrayColl(people);
     * </pre>
     *
     * @param ele      泛型数组
     * @param clazz    当前类
     * @param isLinked 是否是链表集合
     * @return 泛型对象容器
     * @throws IllegalArgumentException 如果容器为空或者类字面常量为空，则会抛出异常
     * @throws NoSuchMethodException    当前类没有默认的构造器
     */
    @SuppressWarnings("all")
    public static <E> Set<E> asSet(E[] ele, Class<? extends E> clazz, boolean isLinked) throws IllegalArgumentException, NoSuchMethodException {
        return isLinked ? new LinkedHashSet<E>(asList(ele, clazz, true)) : new HashSet<E>(asList(ele, clazz, false));
    }

    /**
     * 初始化容量大小。
     *
     * @param collection 容器对象。
     * @return 返回容器的长度大小
     */
    private static int initArrayCapacity(Collection<String> collection) {
        return CollectionUtil.isEmpty(collection) ? 0 : collection.size();
    }
}
